package com.itheima.sms.factory;

import cn.hutool.core.lang.UUID;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;

/**
 * 服务选举器
 */
@Component
@Slf4j
@Order(value = 100)
public class ServerRegister implements CommandLineRunner {
    public static String SERVER_ID = null;

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 实现spring的CommandLineRunner接口, 即可实现项目启动时,自动运行此方法
     * </p>
     * 项目启动时, 会自动执行此方法, 将当前服务实例祖册到redis
     * 使用redis的Hash结构, key为SERVER_ID_HASH, HASH结构的hashKey为id, value为时间戳
     *
     * @param args
     */
    @Override
    public void run(String... args) {
        // 为当前服务实例生成一个服务唯一标识
        SERVER_ID = UUID.randomUUID().toString();
        log.info("生成当前服务实例id: {}", SERVER_ID);
        redisTemplate.opsForHash().put("SERVER_ID_HASH", SERVER_ID, System.currentTimeMillis());
    }

    /**
     * 定时服务报告
     * 报告服务信息证明服务存在 每三分钟报告一次，并传入当前时间戳
     */
    @Scheduled(cron = "1 0/3 * * * ?")
    public void serverReport() {
        long currentTimeMillis = System.currentTimeMillis();
        log.info("定时上报, 服务id: {}, 时间戳: {}", SERVER_ID, currentTimeMillis);

        // 这里的SERVER_ID 已经被run方法赋值了
        redisTemplate.opsForHash().put("SERVER_ID_HASH", SERVER_ID, currentTimeMillis);
    }

    /**
     * 定时服务检查
     * 每十分钟检查一次服务列表，清空超过五分钟没有报告的服务
     */
    @Scheduled(cron = "30 0/10 * * * ?")
    public void checkServer() {
        // 集群中每一个服务都是执行这个检查
        log.info("进行服务实例的检查, 执行当前任务的服务为: {}", SERVER_ID);
        // 获取Redis中注册的所有服务实例id
        Map map = redisTemplate.opsForHash().entries("SERVER_ID_HASH");
        // 获取当前系统时间戳
        long currentTimeMillis = System.currentTimeMillis();

        // key为服务实例的id, value为上报的系统时间戳
        map.forEach((key, value) -> {
            //  (Long) "1231321" --> 是否可以强制成 Long类型
            long parseLong = Long.parseLong(value.toString());
            if ((currentTimeMillis - parseLong) > 1000 * 60 * 5) {
                // 当前服务实例超过5分钟没有上报
                log.info("清理服务实例: {}", key);
                redisTemplate.opsForHash().delete("SERVER_ID_HASH", key);
            }
        });
    }
}

